package cn.b.sky.user.config;

import cn.b.sky.user.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;

/**
 * @Author: yueding
 * @Description:
 * @EnableWebSecurity: 禁用Boot的默认Security配置，配合@Configuration启用自定义配置（需要扩展WebSecurityConfigurerAdapter）
 * @EnableGlobalMethodSecurity(prePostEnabled = true): 启用Security注解，例如最常用的@PreAuthorize configure(HttpSecurity):
 * Request层面的配置，对应XML Configuration中的<http>元素 configure(WebSecurity):
 * Web层面的配置，一般用来配置无需安全检查的路径 configure(AuthenticationManagerBuilder):
 * 身份验证配置，用于注入自定义身份验证Bean和密码校验规则
 * @Date: Created in 17:35 2017/10/31
 * @Modified By: Copyright(c) cai-inc.com
 */
@Configuration
@EnableWebSecurity
public class SkySecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private MyUserDetailsService myUserDetailService;

    @Autowired
    private MyFilterSecurityInterceptor myFilterSecurityInterceptor;

    @Autowired
    private AuthenticationSuccessHandler authenticationSuccessHandler;

    @Autowired
    private MySessionRegistryImpl mySessionRegistry;

    @Bean
    @Primary
    public DefaultWebInvocationPrivilegeEvaluator customWebInvocationPrivilegeEvaluator() {
        return new DefaultWebInvocationPrivilegeEvaluator(myFilterSecurityInterceptor);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        // javaconfig 配置是这样 set 进去的.
        web.securityInterceptor(myFilterSecurityInterceptor);
        web.privilegeEvaluator(customWebInvocationPrivilegeEvaluator());
        web.ignoring().antMatchers("/manage/login", "/manage/loginDialog", "/manage/management/**",
                "c**", "/**/*.jpg", "/**/*.css", "/**/*.js", "/**/*.swf",
                "/**/*.properties", "/**/*.gif", "/**/*.png", "/**/*.ico","/**/*.xml");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().authorizeRequests().antMatchers("/resources", "/login", "/kaptcha/**").permitAll()// 访问：这些路径无需登录认证权限
                .anyRequest().authenticated() // 其他所有资源都需要认证，登陆后访问
                .and().formLogin().loginPage("/manage/doLogin")
                .failureUrl("/manage/login?error=1")
                .usernameParameter("username").passwordParameter("password").permitAll()
                .successHandler(authenticationSuccessHandler) // 登录成功后可使用loginSuccessHandler()存储用户信息，可选。
                .and().logout().logoutUrl("/manage/logout").logoutSuccessUrl("/manage/login") // 退出登录后的默认网址是”/home”.addLogoutHandler(logoutHandler()).permitAll().invalidateHttpSession(true)
                .and().exceptionHandling().authenticationEntryPoint(loginUrlEntryPoint())
                .accessDeniedHandler(accessDeniedHandler()).accessDeniedPage("/manage/accessDenied")
                .and().sessionManagement().maximumSessions(-1)
                .expiredUrl("/login?expired").sessionRegistry(mySessionRegistry);
        // .and()
        // .rememberMe()//登录后记住用户，下次自动登录,数据库中必须存在名为persistent_logins的表
        // .tokenValiditySeconds(1209600);
        http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class);
        //解决frame嵌套问题
        http.headers().frameOptions().sameOrigin();

    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 指定密码加密所使用的加密器为passwordEncoder()
        // 需要将密码加密后写入数据库
        auth.userDetailsService(myUserDetailService).passwordEncoder(passwordEncoder());
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(4);
    }

    @Bean
    public MysecurityContextLogoutHandler logoutHandler() {
        return new MysecurityContextLogoutHandler();
    }

    @Bean
    public AccessDeniedHandler accessDeniedHandler() {
        return new AuthorityAccessDeniedHandlerImpl();
    }

    @Bean
    public BskyLoginUrlEntryPoint loginUrlEntryPoint() {
        return new BskyLoginUrlEntryPoint("/manage/login");
    }

}
